// const { default: compileUtil } = require("./utils/compileUtil")
import compileUtil from './utils/compileUtil.js'
import CompileUtil from './utils/compileUtil.js'

export class Compiler {

  constructor(el, vm) {
    this.el = this.isElementNode(el) ? el : document.querySelector(el)
    this.vm = vm

    //转换成文档片段
    let fragment = this.node2fragment(this.el)
    //编译模板，将真实DOM劫持到文档片段中，开始进行模板编译，用Vue中数据进行替换
    this.compile(fragment)
    //将编译好的模板加回页面中
    this.el.appendChild(fragment)
  }

  /**
   * @desc 判断是否 DOM 元素节点
   * @param {any} node 
   */
  isElementNode(node) {
    return node.nodeType === 1
  }

  isDirective(data) {
    if(data.startsWith('v-')) {
      return true
    }
    return false
  }

  /**
   * @desc 将真实 DOM 劫持到内存中
   * @param {HTMLElement} node 
   */
  node2fragment(node) {
    let fragment = document.createDocumentFragment()
    let firstChild

    while (firstChild = node.firstChild) {
      //appendChild方法在插入节点时，如果被插入节点已存在文档树中，在插入时会先在文档树中删除该节点
      fragment.appendChild(firstChild)
    }
    return fragment
  }

  /**
   * @desc 编译过程
   *       就是遍历文档片段中所有子节点，然后根据子节点类型进行区分
   *       是元素节点，进行元素节点编译；是文本节点，进行文本节点编译；
   * @param {DOM} node 
   */
  compile(node) {
    let childNodes = [].slice.call(node.childNodes)
    childNodes.forEach(child => {
      if(this.isElementNode(child)) {
        this.compileElement(child)
        this.compile(child)
      } else {
        this.compileText(child) //编译文本节点，即 {{ }} mustache 表达式
      }
    })
  }

  /**
   * @desc 对元素节点的编译
   *       处理元素节点上各种属性，如 v-xxx
   * @param {HTMLElement} node 
   */
  compileElement(node) {
    let attributes = [].slice.call(node.attributes)
    attributes.forEach(attr => {
      let { name, value:expr } = attr
      if (this.isDirective(name)) { //是 v-开头
        let [, directive] = name.split('-') //去除 v-
        let [directiveName, eventName] = directive.split(':') //分开指令名和事件名，如 v-on:click 为 on click
        //CompileUtil编译工具对象专门进行各种指令的具体处理
        compileUtil[directiveName](node, expr, this.vm, eventName)
      }
    })
  }

  /**
   * @desc 
   * @param {DOM} node 
   */
  compileText(node) {
    const content = node.textContent
    if(/\{\{(.+?)\}\}/.test(content)) { //检测文本节点中是否含有 {{ }}表达式
      CompileUtil['text'](node, content, this.vm) 
    }
  }
}